package websocket

import (
	"context"
	"encoding/binary"
	"encoding/json"
	"fmt"
	"github.com/gin-gonic/gin"
	"github.com/gorilla/websocket"
	"log"
	"net/http"
	"server/utils/audio"
	"server/utils/db"
	"sync"
	"unicode/utf8"
)

type Client struct {
	ID     int64
	Conn   *websocket.Conn
	Cancel context.CancelFunc
}

var (
	upGrader = websocket.Upgrader{
		CheckOrigin: func(r *http.Request) bool {
			return true // 允许所有连接
		},
	}
	clients      = make(map[int64]*Client)
	clientsMutex = sync.Mutex{}
)

// 用法，使用gin启动的方法
// r := gin.Default()
// r.GET("/api/chat", websocket.Handler)
// 每个调用表示一个客户端连接，for循环结束不会影响其他客户端连接
func Handler(c *gin.Context) {
	conn, err := upGrader.Upgrade(c.Writer, c.Request, nil)
	if err != nil {
		log.Println("[WebSocket] http升级websocket失败", err)
		return
	}
	// 读取第一条消息：客户端的ID
	_, msg, err := conn.ReadMessage()
	if err != nil {
		log.Println("[WebSocket] 接收第一条消息失败:", err)
		_ = conn.Close()
		return
	}
	cmd, fromId, _, _, _, err := parseData(msg)
	if err != nil {
		log.Println(err.Error())
		return
	}
	if cmd != "login" {
		log.Println("[WebSocket] 客户端发送的第一条消息应该是login，现在是:", cmd)
		return
	}
	fmt.Println("[WebSocket] 客户端已连接 id：", fromId)
	handleConnection(fromId, conn)
}

func handleConnection(id int64, conn *websocket.Conn) {
	ctx, cancel := context.WithCancel(context.Background())
	client := &Client{
		ID:     id,
		Conn:   conn,
		Cancel: cancel,
	}
	clientsMutex.Lock()
	clients[id] = client
	clientsMutex.Unlock()

	SendToClient("login_success", 0, id, nil)

	defer func() {
		clientsMutex.Lock()
		delete(clients, id)
		clientsMutex.Unlock()

		_ = conn.Close()
		fmt.Println("[WebSocket] 连接关闭，id:", id)
	}()

	for {
		select {
		case <-ctx.Done():
			return
		default:
			messageType, msg, err := conn.ReadMessage()
			if err != nil {
				log.Println("[WebSocket] 客户端已断开:", err)
				return
			}
			if messageType == websocket.BinaryMessage && len(msg) <= 4 {
				//fmt.Println("收到心跳包")
				continue
			}
			cmd, _, toId, params, data, err := parseData(msg)
			if err != nil {
				log.Println(err.Error())
				continue
			}
			fmt.Println("[WebSocket] 收到消息:", cmd)
			switch cmd {
			case "login":
				SendToClient("login_success", 0, id, nil)
			case "send_audio_to_friend", "send_audio_to_group":
				//保存音效并通知对方
				filePath := params
				err = audio.SaveAudioAsOpus(data, filePath)
				if err != nil {
					log.Println(err.Error())
					continue
				}
				if cmd == "send_audio_to_friend" {
					msgType := 1
					result := db.Exec("insert into private_msgs (sender_id, receiver_id, content, msg_type) VALUES (?, ?, ?, ?)", id, toId, filePath, msgType)
					msgId, _ := result.LastInsertId()
					content := map[string]any{
						"id":       msgId,
						"msg_type": msgType,
						"content":  filePath,
					}
					SendToClient("chat_msg", id, toId, content)
					SendToClient("chat_msg", id, id, content)
				} else {
					/*SendToClient("chat_msg", fromId, groupId, gin.H{
						"file_name": fileName,
						"file_name": fileName,
					})*/
				}
			}
		}
	}
}

func parseData(buf []byte) (cmd string, fromId, toId int64, params string, data []byte, err error) {
	if len(buf) < 12 {
		err = fmt.Errorf("至少需要12字节头部")
		return
	}
	// 读取固定长度头部（大端序）
	fromId = int64(binary.BigEndian.Uint32(buf[0:4]))
	toId = int64(binary.BigEndian.Uint32(buf[4:8]))
	cmdLength := binary.BigEndian.Uint16(buf[8:10])
	paramsLength := binary.BigEndian.Uint16(buf[10:12])
	// 检查数据完整性
	headerSize := 12 // 固定头部长度
	if len(buf) < headerSize+int(cmdLength)+int(paramsLength) {
		err = fmt.Errorf("消息不完整")
		return
	}
	// 提取cmd
	cmd = string(buf[headerSize : headerSize+int(cmdLength)])
	// 提取params
	params = string(buf[headerSize+int(cmdLength) : headerSize+int(cmdLength)+int(paramsLength)])
	// 提取data部分
	dataStart := headerSize + int(cmdLength) + int(paramsLength)
	data = buf[dataStart:]
	return
}

func isUTF8String(data []byte) bool {
	if len(data) == 0 {
		return true
	}
	if utf8.Valid(data) {
		for _, b := range data {
			if b < 32 || b > 126 {
				if !utf8.RuneStart(b) {
					continue
				}
				return false
			}
		}
		return true
	}
	return false
}

func SendToClient(cmd string, fromId, toId int64, data any) {
	clientsMutex.Lock()
	client, ok := clients[toId]
	clientsMutex.Unlock()

	if !ok {
		log.Println(fmt.Sprintf("客户端[id:%d]不在线", toId))
		return
	}

	proto := make(map[string]any)
	proto["cmd"] = cmd
	proto["sender_id"] = fromId
	proto["receiver_id"] = toId
	proto["data"] = data
	msg, _ := json.Marshal(proto)

	fmt.Println("[WebSocket] 给客户端发送消息：", string(msg))

	if err := client.Conn.WriteMessage(websocket.TextMessage, msg); err != nil {
		log.Println("[WebSocket] 消息发送失败，可能客户端已断开", err)
		if websocket.IsUnexpectedCloseError(err, websocket.CloseNormalClosure) {
			fmt.Println("[WebSocket] 释放客户端缓存")
			client.Cancel() //触发handleConnection方法里for退出
		}
	}
}
